package util.evaluation;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;

import udf.matching.MatchQuerySuggestion;
import util.io.FileInput;
import util.io.FileOutput;

public class StatsGenerator {

	/**
	 * @param args
	 */
	
	
	MatchQuerySuggestion matcher = new MatchQuerySuggestion();
	
	Hashtable<String,Integer> query_totals = new Hashtable<String,Integer>();
	Hashtable<String,Integer> pairs_totals = new Hashtable<String,Integer>();
	
	
	int at=0;

	Hashtable<String,Hashtable<String, ArrayList<String>>> gold = new Hashtable<String,Hashtable<String, ArrayList<String>>>();

	private HashSet<String> query_set;
	

	public StatsGenerator(Hashtable<String,Hashtable<String, ArrayList<String>>> gold,  HashSet<String >queries){
		this.query_set= queries;
		this.gold=gold;
		initTotals();
		
	}
	
	
	
	private void initTotals() {
		// TODO Auto-generated method stub
		Enumeration<String> types = gold.keys();
		while(types.hasMoreElements()){
			
			String type = types.nextElement();
			Hashtable<String, ArrayList<String>> querie_e = gold.get(type);
			Enumeration<String> queries = querie_e.keys();
			int total=0;
			while(queries.hasMoreElements()){
				ArrayList<String> lista = querie_e.get(queries.nextElement());
				if(lista!=null)
				total = total + lista.size();
			}
			query_totals.put(type, querie_e.size());
			pairs_totals.put(type, total);
		}
		
	}


	public static double log2(double num)
	{
	return (Math.log(num)/Math.log(2));
	}

	
	public double calculateOptimalDCG(int n){
		double dcg=0.0;
	
		for(int i=1; i<=n;i++){
			
			double score=0;
			if(i==1){
				score=1;
				
			}
			
			dcg+=(Math.pow(2, score)-1.0)/(log2(1+i));
		}
		return dcg;
	}
	
	
	public double calculateNDCG(ArrayList<Integer> scores){
		double dcg=0.0;
		
		double optimal_dcg= calculateOptimalDCG(scores.size()); 
		int n=scores.size();
		for(int i=1; i<=n;i++){
			
			double score = scores.get(i-1);
			
			dcg+=(Math.pow(2, score)-1.0)/(log2(1+i));
		}
		return dcg/optimal_dcg;
	}
	
	public double evaluateNDCG(
			Hashtable<String, ArrayList<String>> suggestions, int top,
			boolean exact) {
		

		Enumeration<String> keys = gold.keys();
		double ndcg = 0;
		double total_pairs = 0;
		while (keys.hasMoreElements()) {

			String age = keys.nextElement();

			Hashtable<String, ArrayList<String>> base = gold.get(age);
			Enumeration<String> queries_gold = base.keys();
			while (queries_gold.hasMoreElements()) {
				String query_gold = queries_gold.nextElement();
				ArrayList<String> gold_ref = base.get(query_gold);
				for (int i = 0; i < gold_ref.size(); i++) {
					if (suggestions.containsKey(query_gold)) {
						ArrayList<String> method_ref = suggestions
								.get(query_gold);
						ArrayList<Integer> scores = new ArrayList<Integer>();

						// get scores for query_gold in methods reformulations
						for (int j = 0; j < method_ref.size() && j < top; j++) {
							int result = 0;
		int k=0;
							if (exact) {
								result = matcher.matchSuggestionApprox(
										query_gold, gold_ref.get(i), method_ref
												.get(j));
							} else {
								result = matcher.matchSuggestionApprox(
										query_gold, gold_ref.get(i), method_ref
												.get(j));
							}
							scores.add(result);
						}

						// calculate ndcg
						double query_ndcg = calculateNDCG(scores);
						total_pairs++;
						ndcg += query_ndcg ;
					
					}
				}

			}
		}
			return ndcg / (double) total_pairs;
		
	}
	
	
	public double evaluateMRR(
			Hashtable<String, ArrayList<String>> suggestions, int top,
			boolean exact) {

		Enumeration<String> keys = gold.keys();
		double ndcg = 0;
		double ranks = 0;
		double total_pairs = 0;
		while (keys.hasMoreElements()) {

			String age = keys.nextElement();

			Hashtable<String, ArrayList<String>> base = gold.get(age);
			Enumeration<String> queries_gold = base.keys();
			while (queries_gold.hasMoreElements()) {
				String query_gold = queries_gold.nextElement();
				ArrayList<String> gold_ref = base.get(query_gold);
				for (int i = 0; i < gold_ref.size(); i++) {
					if (suggestions.containsKey(query_gold)) {
						ArrayList<String> method_ref = suggestions
								.get(query_gold);
						total_pairs++;
						// get scores for query_gold in methods reformulations
						for (int j = 0; j < method_ref.size() && j < top; j++) {
							int result = 0;

							if (exact) {
								result = matcher.matchSuggestionApprox(
										query_gold, gold_ref.get(i), method_ref
												.get(j));
							} else {
								result = matcher.matchSuggestionApprox(
										query_gold, gold_ref.get(i), method_ref
												.get(j));
							}

							if (result == 1) {
								ranks += (double)1.0/(double)(j+1.0);
							//	System.out.println(ranks);
							}

						}

					}
				}

			}
		}
	//	System.out.println(ranks / (double) total_pairs + "\t"+ ranks + "\t "+ total_pairs);
		return ranks / (double) total_pairs;

	}
	
	


	public HashSet<String> searchApproxMatches(String query, String suggestion){
		HashSet<String> results= new HashSet<String>();
		suggestion = suggestion.replace("_", " ").trim();
		Enumeration<String> keys = gold.keys();
		
		while(keys.hasMoreElements()){
			
			String age =keys.nextElement();
			
			Hashtable<String, ArrayList<String>> base = gold.get(age);
			
			ArrayList<String> gold_reformulations;
			if(base.containsKey(query)){
				
				gold_reformulations = base.get(query);
				for(int i=0; i<gold_reformulations.size();i++){
					int result=0;
					result=matcher.matchSuggestionApprox(query, gold_reformulations.get(i), suggestion);
					if(result==1){
					results.add(age);
					}
					
				}
			}
		}
		
		return results;
		
	}
	
	
	
	public Hashtable<String,StatUnit> precision(Hashtable<String, ArrayList<String>> suggestions, int top,  boolean evaluateExact, boolean countQueryOnce){
		Hashtable<String,StatUnit> stats = new Hashtable<String,StatUnit>();
	
     		Enumeration<String> types = gold.keys();

     		
	
     		while(types.hasMoreElements()){
     			
     			String type = types.nextElement();
     			//type="kids";
     			StatUnit unit = new StatUnit();
     			Enumeration<String> queries = gold.get(type).keys();
     			
     			unit.total_queries= gold.get(type).size();
     			
     			while(queries.hasMoreElements()){
     			
     				String query = queries.nextElement();
     				if(query_set!=null && !query_set.contains(query)){
     					     					continue;
     				}
     			//get reformulation from gold standard given query query and type
     			
     			ArrayList<String> gold_reformulations = gold.get(type).get(query);
     			
     			
     			//get reformulations to be evaluated
     			ArrayList<String> lista=null;
     			if(suggestions.containsKey(query)){
     			lista = suggestions.get(query);
     			}
     	
     			
     			boolean keep_counting=true;
     		
     			for(int i =0; lista!=null && i<lista.size() && keep_counting && i<top; i++){
     			
     				String reformulation = lista.get(i);
     				
     				//check for each existent gold reformulation
     				int result=0;
     				int m=0;
     			
     				for(int j =0; j < gold_reformulations.size(); j++){
     			
     				if(evaluateExact){
     					
     			    	result=matcher.matchSuggestionExact(query, gold_reformulations.get(j), reformulation);
     				}else{
     					result=matcher.matchSuggestionApprox(query, gold_reformulations.get(j), reformulation);
     					
     				}
     				
     				}
     				
     				unit.matches= unit.matches+result;
     				unit.total=unit.total+1;
     				
     				if(result!=0){
     					
     					unit.ranks.add(i+1);
     					
     				}
     				if(countQueryOnce && result!=0){
     					keep_counting =false;
     					
     				}
     				
     			}
     		}
     	     		
     			
  		stats.put(type, unit);
     		
     	}
		
		return stats;
	}
		
	public HashSet<String> searchExactMatches(String query, String suggestion){
		HashSet<String> results= new HashSet<String>();
		
		suggestion = suggestion.replace("_", " ").trim();
		Enumeration<String> keys = gold.keys();
		
		while(keys.hasMoreElements()){
			
			String age =keys.nextElement();
			
			Hashtable<String, ArrayList<String>> base = gold.get(age);
			
			ArrayList<String> gold_reformulations;
			if(base.containsKey(query)){
				
				gold_reformulations = base.get(query);
				for(int i=0; i<gold_reformulations.size();i++){
					int result=0;
					
					result=matcher.matchSuggestionExact(query, gold_reformulations.get(i), suggestion);
					if(result==1){
					results.add(age);
					}
					
				}
			}
		}
		
		return results;
		
	}
    public void evaluate(Hashtable<String, ArrayList<String>> suggestions){
	
		System.out.println("R\tType\t\tMatches\t\tTotal_queries\tTotal_attemps\tRecall\tncdg\tMRR");
		System.out.println("R\tExact at 5");
		Hashtable<String, StatUnit> stats = precision(suggestions, 5, true, true);
		
		double ndcg= evaluateNDCG(suggestions,5,true);
		double mrr= evaluateMRR(suggestions,5,true);
		
		printStats(stats,ndcg, mrr,5);
		System.out.println("R\tApprox at 5");
		
		stats = precision(suggestions, 5, false, true);
		ndcg= evaluateNDCG(suggestions,5,false);
		mrr= evaluateMRR(suggestions,5,false);
		
		
		
		printStats(stats,ndcg, mrr,5);
		
		
	/*	System.out.println("R\tExact at 5 all");
		stats = precision(suggestions, 5, true, false);
		ndcg= evaluateNDCG(suggestions,5,true);
		mrr= evaluateMRR(suggestions,5,true);
		printStats(stats,ndcg, mrr,5);
		System.out.println("R\tApprox at 5 all");
		stats = precision(suggestions, 5, false, false);
		ndcg= evaluateNDCG(suggestions,5,false);
		mrr= evaluateMRR(suggestions,5,false);
		printStats(stats,ndcg, mrr,5);*/
		
		
		System.out.println("R\tExact at 10");
		stats = precision(suggestions, 10, true, true);
		ndcg= evaluateNDCG(suggestions,10,true);
		mrr= evaluateMRR(suggestions,10,true);
		printStats(stats,ndcg, mrr,10);
		System.out.println("R\tApprox at 10 ");
		stats =precision(suggestions, 10, false, true);
		ndcg= evaluateNDCG(suggestions,10,false);
		mrr= evaluateMRR(suggestions,10,false);
		printStats(stats,ndcg, mrr,10);
		
		
		/*System.out.println("R\tExact at 10 all");
		stats = precision(suggestions, 10, true, false);
		ndcg= evaluateNDCG(suggestions,10,true);
		mrr= evaluateMRR(suggestions,10,true);
		printStats(stats,ndcg, mrr,10);
		System.out.println("R\tApprox at 10 all");
		stats = precision(suggestions, 10, false, false);
		ndcg= evaluateNDCG(suggestions,10,false);
		mrr= evaluateMRR(suggestions,10,false);
		printStats(stats,ndcg, mrr,10);*/
		
		
		System.out.println("R\tExact at 100");
		stats = precision(suggestions, 100, true, true);
		ndcg= evaluateNDCG(suggestions,100,true);
		mrr= evaluateMRR(suggestions,100,true);
		printStats(stats,ndcg, mrr,100);
		System.out.println("R\tApprox at 100 ");
		stats = precision(suggestions, 100, false, true);
		ndcg= evaluateNDCG(suggestions,100,false);
		mrr= evaluateMRR(suggestions,100,false);
		printStats(stats,ndcg, mrr,100);
		
		
		/*System.out.println("R\tExact at 100 all");
		stats = precision(suggestions, 100, true, false);
		ndcg= evaluateNDCG(suggestions,100,true);
		mrr= evaluateMRR(suggestions,100,true);
		
		printStats(stats,ndcg, mrr,100);
		System.out.println("R\tApprox at 100 all");
		stats = precision(suggestions, 100, false, false);
		ndcg= evaluateNDCG(suggestions,100,false);
		mrr= evaluateMRR(suggestions,100,false);
		printStats(stats,ndcg, mrr,100);
		*/
		
		
	}


public void printStats(Hashtable<String, StatUnit> stats, double ndcg, double mrr,int at){
	
	
	Enumeration<String> keys = stats.keys();
	
	while(keys.hasMoreElements()){
		String type = keys.nextElement();
		StatUnit unit  = stats.get(type);
		
		/**
		 * Change this !!!
		 * 
		 * 
		 */
		unit.total_queries=query_totals.get(type);
		unit.total=pairs_totals.get(type)*at;
		System.out.println("RESULTS\t"+type + "\t\t"+ unit.matches + "\t\t"+ unit.total_queries + "\t\t" + unit.total + "\t\t" +unit.precision() + "\t"+ndcg + "\t"+ mrr );
		
	}
	
}

	

}

